#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Java架构师学习Anki卡片生成器
用于将学习内容转换为Anki卡片格式，支持导入到Anki中进行复习

使用方法:
1. python anki-card-generator.py --topic "Spring Boot" --content "学习内容"
2. python anki-card-generator.py --file "学习笔记.md"
3. python anki-card-generator.py --interactive

作者: Java架构师学习助手
日期: 2025-09-29
"""

import json
import csv
import re
import argparse
import os
from datetime import datetime
from typing import List, Dict, Any
from dataclasses import dataclass, asdict
import hashlib

@dataclass
class AnkiCard:
    """Anki卡片数据结构"""
    front: str                # 卡片正面（问题）
    back: str                # 卡片背面（答案）
    tags: List[str]          # 标签列表
    card_type: str           # 卡片类型
    difficulty: int          # 难度等级 1-5
    source: str              # 来源
    created_date: str        # 创建日期
    card_id: str             # 卡片唯一ID

class AnkiCardGenerator:
    """Anki卡片生成器"""
    
    # 技能领域映射
    SKILL_DOMAINS = {
        'java': 'Java核心技术',
        'spring': '框架技术', 
        'database': '数据库技术',
        'distributed': '分布式系统',
        'container': '容器化技术',
        'mq': '消息队列',
        'bigdata': '大数据技术',
        'cloud': '云服务技术',
        'architecture': '架构设计',
        'interview': '面试准备'
    }
    
    # 卡片类型
    CARD_TYPES = {
        'concept': '概念理解',
        'code': '代码实现',
        'scenario': '场景应用',
        'interview': '面试问答',
        'practice': '实战经验',
        'compare': '对比分析'
    }
    
    def __init__(self, output_dir: str = "anki_cards"):
        self.output_dir = output_dir
        self.cards: List[AnkiCard] = []
        
        # 确保输出目录存在
        os.makedirs(output_dir, exist_ok=True)
    
    def generate_card_id(self, front: str, back: str) -> str:
        """生成唯一卡片ID"""
        content = f"{front}:{back}:{datetime.now().strftime('%Y%m%d')}"
        return hashlib.md5(content.encode('utf-8')).hexdigest()[:8]
    
    def create_concept_card(self, concept: str, definition: str, 
                          examples: List[str] = None, 
                          tags: List[str] = None) -> AnkiCard:
        """创建概念理解卡片"""
        
        front = f"什么是{concept}？"
        
        back_parts = [f"**{concept}**：{definition}"]
        
        if examples:
            back_parts.append("\n**示例：**")
            for i, example in enumerate(examples, 1):
                back_parts.append(f"{i}. {example}")
        
        back = "\n".join(back_parts)
        
        default_tags = ['概念理解', datetime.now().strftime('%Y-%m-%d')]
        card_tags = tags + default_tags if tags else default_tags
        
        return AnkiCard(
            front=front,
            back=back,
            tags=card_tags,
            card_type='concept',
            difficulty=2,
            source='manual',
            created_date=datetime.now().isoformat(),
            card_id=self.generate_card_id(front, back)
        )
    
    def create_code_card(self, question: str, code: str, 
                        explanation: str = "", 
                        tags: List[str] = None) -> AnkiCard:
        """创建代码实现卡片"""
        
        back_parts = [f"```java\n{code}\n```"]
        
        if explanation:
            back_parts.extend(["\n**解释：**", explanation])
        
        back = "\n".join(back_parts)
        
        default_tags = ['代码实现', datetime.now().strftime('%Y-%m-%d')]
        card_tags = tags + default_tags if tags else default_tags
        
        return AnkiCard(
            front=question,
            back=back,
            tags=card_tags,
            card_type='code',
            difficulty=3,
            source='manual',
            created_date=datetime.now().isoformat(),
            card_id=self.generate_card_id(question, back)
        )
    
    def create_interview_card(self, question: str, answer: str,
                            key_points: List[str] = None,
                            follow_up: List[str] = None,
                            tags: List[str] = None) -> AnkiCard:
        """创建面试问答卡片"""
        
        back_parts = [answer]
        
        if key_points:
            back_parts.append("\n**关键要点：**")
            for point in key_points:
                back_parts.append(f"• {point}")
        
        if follow_up:
            back_parts.append("\n**可能的追问：**")
            for question in follow_up:
                back_parts.append(f"• {question}")
        
        back = "\n".join(back_parts)
        
        default_tags = ['面试准备', datetime.now().strftime('%Y-%m-%d')]
        card_tags = tags + default_tags if tags else default_tags
        
        return AnkiCard(
            front=question,
            back=back,
            tags=card_tags,
            card_type='interview',
            difficulty=4,
            source='manual',
            created_date=datetime.now().isoformat(),
            card_id=self.generate_card_id(question, back)
        )
    
    def create_scenario_card(self, scenario: str, solution: str,
                           technologies: List[str] = None,
                           considerations: List[str] = None,
                           tags: List[str] = None) -> AnkiCard:
        """创建场景应用卡片"""
        
        front = f"**业务场景：**\n{scenario}\n\n**如何设计解决方案？**"
        
        back_parts = [f"**解决方案：**\n{solution}"]
        
        if technologies:
            back_parts.append("\n**技术选型：**")
            for tech in technologies:
                back_parts.append(f"• {tech}")
        
        if considerations:
            back_parts.append("\n**设计考虑：**")
            for consideration in considerations:
                back_parts.append(f"• {consideration}")
        
        back = "\n".join(back_parts)
        
        default_tags = ['场景设计', datetime.now().strftime('%Y-%m-%d')]
        card_tags = tags + default_tags if tags else default_tags
        
        return AnkiCard(
            front=front,
            back=back,
            tags=card_tags,
            card_type='scenario',
            difficulty=5,
            source='manual',
            created_date=datetime.now().isoformat(),
            card_id=self.generate_card_id(front, back)
        )
    
    def create_comparison_card(self, title: str, items: List[Dict[str, str]],
                             conclusion: str = "", tags: List[str] = None) -> AnkiCard:
        """创建对比分析卡片"""
        
        front = f"{title} - 请对比分析"
        
        back_parts = [f"**{title}**\n"]
        
        for item in items:
            back_parts.append(f"**{item['name']}：**")
            back_parts.append(f"• 优点：{item.get('pros', '')}")
            back_parts.append(f"• 缺点：{item.get('cons', '')}")
            back_parts.append(f"• 适用场景：{item.get('scenarios', '')}")
            back_parts.append("")
        
        if conclusion:
            back_parts.extend([f"**总结：**", conclusion])
        
        back = "\n".join(back_parts)
        
        default_tags = ['对比分析', datetime.now().strftime('%Y-%m-%d')]
        card_tags = tags + default_tags if tags else default_tags
        
        return AnkiCard(
            front=front,
            back=back,
            tags=card_tags,
            card_type='compare',
            difficulty=4,
            source='manual',
            created_date=datetime.now().isoformat(),
            card_id=self.generate_card_id(front, back)
        )
    
    def add_card(self, card: AnkiCard):
        """添加卡片到集合"""
        self.cards.append(card)
    
    def parse_markdown_content(self, content: str, source_file: str = "") -> List[AnkiCard]:
        """解析Markdown内容生成卡片"""
        cards = []
        
        # 提取代码块
        code_pattern = r'```java\n(.*?)\n```'
        code_blocks = re.findall(code_pattern, content, re.DOTALL)
        
        for i, code in enumerate(code_blocks):
            question = f"请实现以下功能的代码（代码块 {i+1}）"
            card = self.create_code_card(
                question=question,
                code=code.strip(),
                tags=[source_file, 'Markdown解析']
            )
            cards.append(card)
        
        # 提取标题作为概念卡片
        title_pattern = r'^#{1,3}\s+(.+)$'
        titles = re.findall(title_pattern, content, re.MULTILINE)
        
        for title in titles:
            if len(title.strip()) > 0:
                # 尝试提取该标题下的内容作为定义
                title_content = self.extract_content_under_title(content, title)
                if title_content:
                    card = self.create_concept_card(
                        concept=title.strip(),
                        definition=title_content[:200] + "..." if len(title_content) > 200 else title_content,
                        tags=[source_file, 'Markdown解析']
                    )
                    cards.append(card)
        
        return cards
    
    def extract_content_under_title(self, content: str, title: str) -> str:
        """提取标题下的内容"""
        lines = content.split('\n')
        start_index = -1
        
        for i, line in enumerate(lines):
            if title in line and line.startswith('#'):
                start_index = i + 1
                break
        
        if start_index == -1:
            return ""
        
        content_lines = []
        for i in range(start_index, len(lines)):
            line = lines[i]
            if line.startswith('#'):  # 遇到下一个标题就停止
                break
            if line.strip():  # 非空行
                content_lines.append(line)
        
        return '\n'.join(content_lines[:3])  # 只取前3行
    
    def export_to_csv(self, filename: str = None):
        """导出为CSV格式，可导入Anki"""
        if not filename:
            filename = f"anki_cards_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
        
        filepath = os.path.join(self.output_dir, filename)
        
        with open(filepath, 'w', newline='', encoding='utf-8') as csvfile:
            writer = csv.writer(csvfile, quoting=csv.QUOTE_ALL)
            
            # 写入表头
            writer.writerow(['Front', 'Back', 'Tags'])
            
            # 写入卡片数据
            for card in self.cards:
                tags_str = ' '.join(card.tags)
                writer.writerow([card.front, card.back, tags_str])
        
        print(f"已导出 {len(self.cards)} 张卡片到: {filepath}")
        return filepath
    
    def export_to_json(self, filename: str = None):
        """导出为JSON格式，便于管理"""
        if not filename:
            filename = f"anki_cards_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
        
        filepath = os.path.join(self.output_dir, filename)
        
        data = {
            'export_date': datetime.now().isoformat(),
            'total_cards': len(self.cards),
            'cards': [asdict(card) for card in self.cards]
        }
        
        with open(filepath, 'w', encoding='utf-8') as jsonfile:
            json.dump(data, jsonfile, ensure_ascii=False, indent=2)
        
        print(f"已导出 {len(self.cards)} 张卡片到: {filepath}")
        return filepath
    
    def generate_statistics(self) -> Dict[str, Any]:
        """生成统计信息"""
        if not self.cards:
            return {}
        
        # 按类型统计
        type_stats = {}
        for card in self.cards:
            type_stats[card.card_type] = type_stats.get(card.card_type, 0) + 1
        
        # 按难度统计
        difficulty_stats = {}
        for card in self.cards:
            difficulty_stats[card.difficulty] = difficulty_stats.get(card.difficulty, 0) + 1
        
        # 按标签统计
        tag_stats = {}
        for card in self.cards:
            for tag in card.tags:
                tag_stats[tag] = tag_stats.get(tag, 0) + 1
        
        return {
            'total_cards': len(self.cards),
            'by_type': type_stats,
            'by_difficulty': difficulty_stats,
            'by_tags': dict(sorted(tag_stats.items(), key=lambda x: x[1], reverse=True)[:10]),
            'creation_date': datetime.now().isoformat()
        }
    
    def interactive_mode(self):
        """交互式创建卡片"""
        print("=== Java架构师学习Anki卡片生成器 ===")
        print("输入 'quit' 退出，'help' 查看帮助")
        
        while True:
            print("\n请选择卡片类型：")
            print("1. 概念理解卡片")
            print("2. 代码实现卡片")
            print("3. 面试问答卡片")
            print("4. 场景应用卡片")
            print("5. 对比分析卡片")
            print("6. 导出卡片")
            print("7. 查看统计")
            
            choice = input("\n请选择 (1-7): ").strip()
            
            try:
                if choice == '1':
                    self.create_concept_card_interactive()
                elif choice == '2':
                    self.create_code_card_interactive()
                elif choice == '3':
                    self.create_interview_card_interactive()
                elif choice == '4':
                    self.create_scenario_card_interactive()
                elif choice == '5':
                    self.create_comparison_card_interactive()
                elif choice == '6':
                    self.export_cards_interactive()
                elif choice == '7':
                    self.show_statistics()
                elif choice.lower() in ['quit', 'q', '退出']:
                    print("再见！")
                    break
                elif choice.lower() in ['help', 'h', '帮助']:
                    self.show_help()
                else:
                    print("无效选择，请重新输入")
                    
            except KeyboardInterrupt:
                print("\n\n程序被中断，再见！")
                break
            except Exception as e:
                print(f"发生错误: {e}")
    
    def create_concept_card_interactive(self):
        """交互式创建概念卡片"""
        print("\n=== 创建概念理解卡片 ===")
        
        concept = input("概念名称: ").strip()
        if not concept:
            print("概念名称不能为空")
            return
        
        definition = input("概念定义: ").strip()
        if not definition:
            print("概念定义不能为空")
            return
        
        # 示例（可选）
        examples = []
        print("添加示例（按回车跳过，输入 'done' 结束）:")
        while True:
            example = input(f"示例 {len(examples)+1}: ").strip()
            if not example or example.lower() == 'done':
                break
            examples.append(example)
        
        # 标签
        tags_input = input("标签（用空格分隔，可选）: ").strip()
        tags = tags_input.split() if tags_input else []
        
        card = self.create_concept_card(concept, definition, examples, tags)
        self.add_card(card)
        
        print(f"✅ 已创建概念卡片: {concept}")
    
    def create_code_card_interactive(self):
        """交互式创建代码卡片"""
        print("\n=== 创建代码实现卡片 ===")
        
        question = input("代码问题: ").strip()
        if not question:
            print("代码问题不能为空")
            return
        
        print("请输入代码（输入 'END' 结束）:")
        code_lines = []
        while True:
            line = input()
            if line.strip() == 'END':
                break
            code_lines.append(line)
        
        code = '\n'.join(code_lines)
        if not code.strip():
            print("代码内容不能为空")
            return
        
        explanation = input("代码解释（可选）: ").strip()
        
        tags_input = input("标签（用空格分隔，可选）: ").strip()
        tags = tags_input.split() if tags_input else []
        
        card = self.create_code_card(question, code, explanation, tags)
        self.add_card(card)
        
        print(f"✅ 已创建代码卡片: {question[:50]}...")
    
    def create_interview_card_interactive(self):
        """交互式创建面试卡片"""
        print("\n=== 创建面试问答卡片 ===")
        
        question = input("面试问题: ").strip()
        if not question:
            print("面试问题不能为空")
            return
        
        answer = input("参考答案: ").strip()
        if not answer:
            print("参考答案不能为空")
            return
        
        # 关键要点
        key_points = []
        print("关键要点（按回车跳过，输入 'done' 结束）:")
        while True:
            point = input(f"要点 {len(key_points)+1}: ").strip()
            if not point or point.lower() == 'done':
                break
            key_points.append(point)
        
        # 追问
        follow_up = []
        print("可能的追问（按回车跳过，输入 'done' 结束）:")
        while True:
            question_fu = input(f"追问 {len(follow_up)+1}: ").strip()
            if not question_fu or question_fu.lower() == 'done':
                break
            follow_up.append(question_fu)
        
        tags_input = input("标签（用空格分隔，可选）: ").strip()
        tags = tags_input.split() if tags_input else []
        
        card = self.create_interview_card(question, answer, key_points, follow_up, tags)
        self.add_card(card)
        
        print(f"✅ 已创建面试卡片: {question[:50]}...")
    
    def create_scenario_card_interactive(self):
        """交互式创建场景卡片"""
        print("\n=== 创建场景应用卡片 ===")
        
        scenario = input("业务场景: ").strip()
        if not scenario:
            print("业务场景不能为空")
            return
        
        solution = input("解决方案: ").strip()
        if not solution:
            print("解决方案不能为空")
            return
        
        # 技术选型
        technologies = []
        print("技术选型（按回车跳过，输入 'done' 结束）:")
        while True:
            tech = input(f"技术 {len(technologies)+1}: ").strip()
            if not tech or tech.lower() == 'done':
                break
            technologies.append(tech)
        
        # 设计考虑
        considerations = []
        print("设计考虑（按回车跳过，输入 'done' 结束）:")
        while True:
            consideration = input(f"考虑 {len(considerations)+1}: ").strip()
            if not consideration or consideration.lower() == 'done':
                break
            considerations.append(consideration)
        
        tags_input = input("标签（用空格分隔，可选）: ").strip()
        tags = tags_input.split() if tags_input else []
        
        card = self.create_scenario_card(scenario, solution, technologies, considerations, tags)
        self.add_card(card)
        
        print(f"✅ 已创建场景卡片: {scenario[:50]}...")
    
    def create_comparison_card_interactive(self):
        """交互式创建对比卡片"""
        print("\n=== 创建对比分析卡片 ===")
        
        title = input("对比主题: ").strip()
        if not title:
            print("对比主题不能为空")
            return
        
        items = []
        print("添加对比项（至少2个）:")
        
        while len(items) < 2:
            name = input(f"项目 {len(items)+1} 名称: ").strip()
            if not name:
                continue
            
            pros = input("  优点: ").strip()
            cons = input("  缺点: ").strip()
            scenarios = input("  适用场景: ").strip()
            
            items.append({
                'name': name,
                'pros': pros,
                'cons': cons,
                'scenarios': scenarios
            })
        
        # 继续添加更多项目
        while True:
            name = input(f"项目 {len(items)+1} 名称（回车结束）: ").strip()
            if not name:
                break
            
            pros = input("  优点: ").strip()
            cons = input("  缺点: ").strip()
            scenarios = input("  适用场景: ").strip()
            
            items.append({
                'name': name,
                'pros': pros,
                'cons': cons,
                'scenarios': scenarios
            })
        
        conclusion = input("总结（可选）: ").strip()
        
        tags_input = input("标签（用空格分隔，可选）: ").strip()
        tags = tags_input.split() if tags_input else []
        
        card = self.create_comparison_card(title, items, conclusion, tags)
        self.add_card(card)
        
        print(f"✅ 已创建对比卡片: {title}")
    
    def export_cards_interactive(self):
        """交互式导出卡片"""
        if not self.cards:
            print("没有卡片可导出")
            return
        
        print(f"\n当前有 {len(self.cards)} 张卡片")
        print("选择导出格式：")
        print("1. CSV格式（可导入Anki）")
        print("2. JSON格式（便于管理）")
        print("3. 两种格式都导出")
        
        choice = input("请选择 (1-3): ").strip()
        
        if choice == '1':
            self.export_to_csv()
        elif choice == '2':
            self.export_to_json()
        elif choice == '3':
            self.export_to_csv()
            self.export_to_json()
        else:
            print("无效选择")
    
    def show_statistics(self):
        """显示统计信息"""
        if not self.cards:
            print("暂无卡片")
            return
        
        stats = self.generate_statistics()
        
        print(f"\n=== 卡片统计 ===")
        print(f"总卡片数: {stats['total_cards']}")
        
        print(f"\n按类型分布:")
        for card_type, count in stats['by_type'].items():
            type_name = self.CARD_TYPES.get(card_type, card_type)
            print(f"  {type_name}: {count}")
        
        print(f"\n按难度分布:")
        for difficulty, count in stats['by_difficulty'].items():
            print(f"  难度{difficulty}: {count}")
        
        print(f"\n热门标签:")
        for tag, count in list(stats['by_tags'].items())[:5]:
            print(f"  {tag}: {count}")
    
    def show_help(self):
        """显示帮助信息"""
        help_text = """
=== 帮助信息 ===

1. 概念理解卡片：用于记录技术概念、定义和要点
2. 代码实现卡片：用于记录代码示例和实现方法
3. 面试问答卡片：用于准备技术面试，包含问题和详细答案
4. 场景应用卡片：用于记录业务场景和解决方案
5. 对比分析卡片：用于技术选型和方案对比

导出的CSV文件可以直接导入到Anki中使用。
建议定期导出备份，避免数据丢失。

标签使用技巧：
- 使用技术领域标签：Java、Spring、MySQL等
- 使用难度标签：初级、中级、高级
- 使用时间标签：2025-09、Week42等
"""
        print(help_text)

def main():
    """主函数"""
    parser = argparse.ArgumentParser(description='Java架构师学习Anki卡片生成器')
    parser.add_argument('--interactive', '-i', action='store_true', 
                       help='启动交互式模式')
    parser.add_argument('--file', '-f', type=str,
                       help='从Markdown文件生成卡片')
    parser.add_argument('--output', '-o', type=str, default='anki_cards',
                       help='输出目录（默认：anki_cards）')
    
    args = parser.parse_args()
    
    generator = AnkiCardGenerator(output_dir=args.output)
    
    if args.interactive:
        generator.interactive_mode()
    elif args.file:
        if os.path.exists(args.file):
            with open(args.file, 'r', encoding='utf-8') as f:
                content = f.read()
            
            cards = generator.parse_markdown_content(content, os.path.basename(args.file))
            for card in cards:
                generator.add_card(card)
            
            print(f"从 {args.file} 生成了 {len(cards)} 张卡片")
            generator.export_to_csv()
            generator.export_to_json()
        else:
            print(f"文件不存在: {args.file}")
    else:
        # 演示模式，创建一些示例卡片
        print("演示模式：创建示例卡片")
        
        # 示例：概念卡片
        concept_card = generator.create_concept_card(
            concept="Spring Boot自动配置",
            definition="Spring Boot通过条件注解和自动配置类，根据classpath中的依赖自动配置Bean",
            examples=[
                "@ConditionalOnClass注解检查类是否存在",
                "@ConditionalOnProperty注解检查配置属性",
                "spring.factories文件定义自动配置类"
            ],
            tags=["SpringBoot", "自动配置", "中级"]
        )
        generator.add_card(concept_card)
        
        # 示例：代码卡片
        code_card = generator.create_code_card(
            question="如何实现Spring Boot自定义启动器？",
            code="""@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
    
    @Autowired
    private MyProperties properties;
    
    @Bean
    @ConditionalOnMissingBean
    public MyService myService() {
        return new MyService(properties);
    }
}""",
            explanation="通过@Configuration和条件注解实现自动配置",
            tags=["SpringBoot", "自动配置", "代码实现"]
        )
        generator.add_card(code_card)
        
        # 示例：面试卡片
        interview_card = generator.create_interview_card(
            question="Spring Boot是如何实现自动配置的？",
            answer="Spring Boot通过@EnableAutoConfiguration注解，扫描META-INF/spring.factories文件中定义的自动配置类，结合条件注解进行有选择的Bean注册。",
            key_points=[
                "@EnableAutoConfiguration启用自动配置",
                "spring.factories定义配置类列表",
                "条件注解控制配置生效条件",
                "AutoConfigurationImportSelector负责加载配置类"
            ],
            follow_up=[
                "条件注解有哪些类型？",
                "如何自定义自动配置类？",
                "自动配置的执行顺序如何控制？"
            ],
            tags=["SpringBoot", "面试", "自动配置"]
        )
        generator.add_card(interview_card)
        
        print(f"创建了 {len(generator.cards)} 张示例卡片")
        generator.export_to_csv()
        generator.export_to_json()
        generator.show_statistics()

if __name__ == '__main__':
    main()
